#include "head.h"
#include "Scythe.h"

#include "utilityfunctions.h"
#include "ogreSkeletonSetup.h"



/*
MAIN
This is where you need to really look, this page represents your own program flow and will be the functions
you need to create for yourself if you use different engines
*/
//SETTINGS, set these for your project to run properly
const bool	HW		= true;
//string		pathRoot	= "C:\\Scythe Tech-demo PhysX\\";
string		pathRoot	= "H:\\Devel\\#GameDevTools\\#Physics Engines\\Scythe\\ScytheAPI_1-14\\ScytheAPI_1-14\\Scythe Tech-demo PhysX\\bin";


//first we need an instance of our loader class
Scythe::PhysicsClass physics;
NxScene*		nWorld;
NxPhysicsSDK*	physicsSDK;

//define our callback functions
void storeJointCallback(NxJoint* joint, const Scythe::PhysicsJoint* data, void* userData);
void storeBodyCallback(Scythe::PhysicalObject*, NxActor* body, const Scythe::PhysicsActor* data, void* userData, int skelID);
void loadModelCallback(Scythe::PhysicalObject*, const char* modelName, const Scythe::PhysicsModel* data, const int id, const char* boneName);
void loadSkeletalCallback(Scythe::PhysicalObject* ,const char*, const Scythe::PhysicsModel*, void*, Scythe::SkeletonData*, const Scythe::Vector3, const Scythe::Matrix);//Scythe::PhysicalObject*, const char* modelName, const Scythe::PhysicsModel* data, void* userData, const Scythe::SkeletonData* skelID, const Scythe::Vector3 position, const Scythe::Matrix rotation);
void loadLightCallback(Scythe::PhysicalObject*, const Scythe::PhysicsLight* data, const int id);


//This is a class that represents your own entity classes (see head.h)
//an encapsulation of a whole .phs file construction.  
class PhysicsObject : public Scythe::PhysicalObject
{
public:
	PhysicsObject(){}
	~PhysicsObject() {		//use this to destroy your entities
		for (int i = 0 ; i < ents.size(); i++) {
			///render.destroy(ents[i]->ent);
			delete ents[i];
		}
		ents.clear();

		for (int i = 0 ; i < lights.size(); i++) {
			///render.destroy(lights[i]->light);
			delete lights[i] ;
		}
		lights.clear();
		//this is the root class's destroy function
		destroy(nWorld);
	}


	vector<AnEntityClass*>	ents;
	vector<ALightClass*>	lights;

	void			update();
};

std::vector<PhysicsObject*>		models;		//our list of entities



//This represents a character class in your game
//what we have also done here is made a ScytheSkeleton class, which we use to organise our bones and
//do the ragdoll calculations (see ogreSkeletonSetup.h)
//as with normal entities you need an ID value to associate with Scythe (skeletalID is in the ScytheSkeleton class)
class ACharacterClass
{
public:
	ScytheSkeleton		skeleton;
};
ACharacterClass* player;



/////////////////////////////
// for newton, we use these to create breakable joints
struct JointData
{
	bool	breakable;
	float	maxForce;

	bool	limitsOn;
	float	limits[2];
};



//this is your init function called at level startup, this demonstrates how you load
//physics objects from file
void initialisation()
{
	//---
	//First we must initialise our physics engine
	//---
	physicsSDK = NxCreatePhysicsSDK(NX_PHYSICS_SDK_VERSION);
	if(!physicsSDK)	return;
	// Create a scene
	NxSceneDesc sceneDesc;
	sceneDesc.gravity				= NxVec3(0.0f, -9.81, 0.0f);
	sceneDesc.simType			= NX_SIMULATION_SW;
	if(HW) {
		if(physicsSDK->getHWVersion() != NX_HW_VERSION_NONE) {
			sceneDesc.simType	= NX_SIMULATION_HW;
		} 
	}
	nWorld = physicsSDK->createScene(sceneDesc);
	if(!nWorld) return;

	nWorld->setTiming(1.0f/60.0f, 8);
	physicsSDK->setParameter(NX_SKIN_WIDTH, 0.01f);
	NxMaterial*	defaultMaterial = nWorld->getMaterialFromIndex(0);
	defaultMaterial->setRestitution(0.05f);
	defaultMaterial->setStaticFriction(0.4f);
	defaultMaterial->setDynamicFriction(0.4f);

	//----------------------------------------------------------------







	//---
	//We setup the loader class with our callback functions
	//---
	physics.setBodyCreatedCallback( storeBodyCallback );
	physics.setJointCreatedCallback( storeJointCallback );
	physics.setLoadModelCallback( loadModelCallback );	//this is the important one
	physics.setLoadLightCallback( loadLightCallback );	//only needed if you want lights
	physics.setLoadSkeletalModelCallback( loadSkeletalCallback );	//only needed if you want ragdolls


	//Here we create our own physics entity classes that are
	//inherited from Scythe::PhysicalObject.  We then keep track 
	//of them, and can update them or destroy them whenever we want
	PhysicsObject* cratesObj = new PhysicsObject;
	PhysicsObject* cratesObj2 = new PhysicsObject;
	PhysicsObject* windmillObj = new PhysicsObject;
	PhysicsObject* groundObj = new PhysicsObject;
	PhysicsObject* ragdollObj = new PhysicsObject;
	//put them all in a global list
	models.push_back(cratesObj);
	models.push_back(cratesObj2);
	models.push_back(windmillObj);
	models.push_back(groundObj);
	models.push_back(ragdollObj);

	//---
	//Once setup, this is how you load a physics file
	//---
	//instance a PhysicsEntity class
	Scythe::PhysicsEntity* crates = new Scythe::PhysicsEntity;
	//we load a file into memory
	crates->loadFile("data\\Stack of crates.phs");
	//then we insert it into the scene
	physics.insert(cratesObj, nWorld, crates, Scythe::Vector3(4,1,-14));
	//the PhysicsEntity class can be used again to instance more entities
	physics.insert(cratesObj2, nWorld, crates, Scythe::Vector3(-4,0,-14));

	//we can load another file too
	Scythe::PhysicsEntity* windmill = new Scythe::PhysicsEntity;
	//we load a file into memory
	windmill->loadFile("data\\windmill.phs");
	//then we insert it into the scene
	physics.insert(windmillObj, nWorld, windmill, Scythe::Vector3(-0,1,-10));

	//you can even load a static level (in this case, just a flat ground)
	Scythe::PhysicsEntity* ground = new Scythe::PhysicsEntity;
	ground->loadFile("data\\ground.phs");
	physics.insert(groundObj, nWorld, ground, Scythe::Vector3(0,0,0));
/**/
	//////////////////////////////////////////////////////////////////////////
	//			RAGDOLL
	//////////////////////////////////////////////////////////////////////////

	Scythe::PhysicsEntity* ragdoll = new Scythe::PhysicsEntity;
	ragdoll->loadFile("data\\zombie.phs");
	//ragdoll->~PhysicsEntity

	physics.insert(ragdollObj, nWorld, ragdoll, Scythe::Vector3(-3,0,0));
	//ragdoll character has now been created, before update we want to set the ragdoll
	//to match up the the character's current pose (the character may be in mid-stride, 
	//and so we want the ragdoll to start from that position.  We use a non API function here,
	//but it uses the API's setBonePosition() to position the ragdoll.
	player->skeleton.setRagdollToSkeleton();		//player variable was initialised in the ragdoll callback, but it doesnt have to


/**/
}



//your main update function, called every frame
void mainLoop()
{
	//update your physics engine
	nWorld->simulate(1.0f/40.0f);
	nWorld->flushStream();
	nWorld->fetchResults(NX_RIGID_BODY_FINISHED, true);


	//we update each object in the game
	for(unsigned int i = 0; i < models.size(); i++)
	{
		models[i]->update();
	}

	//run the ragdoll calculations
	player->skeleton.updateRagdoll();

}



void PhysicsObject::update()
{
	//we update each object in the game
	//we ask scythe to give us the position and rotation for each model
	//we do this by giving it the ID we got in the loadModelCallback
	for(unsigned int i = 0; i < ents.size(); i++)
	{
		//get position
		Scythe::Vector3 pos = getModelPos( ents[i]->scytheID );
		//set position
		ents[i]->node->setPosition( pos[0], pos[1], pos[2] );
		//get orientation
		Scythe::Quat rot = getModelRotQuat( ents[i]->scytheID );
		//set orientation
		ents[i]->node->setOrientation(rot.w, rot.x, rot.y, rot.z);
	}
	//same again for our lights, we can use the same functions
	for(unsigned int i = 0; i < lights.size(); i++)
	{
		//get position
		Scythe::Vector3 pos = getModelPos( lights[i]->scytheID );
		//set position
		lights[i]->node->setPosition( pos[0], pos[1], pos[2] );
		//get orientation
		Scythe::Quat rot = getModelRotQuat( lights[i]->scytheID );
		//set orientation
		lights[i]->node->setOrientation(rot.w, rot.x, rot.y, rot.z);
	}
}


///////////////////////////////////////////////
//			SCYTHE CALLBACKS
//////////////////////////////////////////////

//called when a new rigidbody is created
void storeBodyCallback(Scythe::PhysicalObject*, NxActor* body, const Scythe::PhysicsActor* data, void* userData, int skelID)
{
	//Only actors know if they are meant to be attached to a ragdolls bone or not
	//so if this actor is, here we tell the bone that it is ragdoll controlled
	if(data->boneModelID > -1)	//note: skelID only valid if this is also valid
	{
		//in this case we know it will be "player", but when you have lots of ragdolls you will
		//obviously need a way to look up the right character using the skelID
		player->skeleton.setBoneIsRag(true, data->boneName);
	}
}



//called when a new joint is created
void storeJointCallback(NxJoint* joint, const Scythe::PhysicsJoint* data, void* userData)
{

}



// when a new model needs to be created, scythe tells you here, and you load the model yourself
void loadModelCallback(Scythe::PhysicalObject* physObj, const char* modelName, const Scythe::PhysicsModel* data, const int id, const char* boneName)
{
	//load the model
	Ogre::Entity* ent = loadModel(modelName, toOgre(data->pos));

	//every game has some kind of class that represents an entity, or a character etc.
	//so here we create a new one and let it know its model id
	AnEntityClass* newEntity = new AnEntityClass;
	newEntity->ent			= ent;
	newEntity->node		= ent->getParentSceneNode();
	newEntity->scytheID		= id;

	//add it to our physics object class
	PhysicsObject* po = (PhysicsObject*)physObj;		//need to cast it to your own type
	po->ents.push_back( newEntity );

	//scale the model, although scaling of models should generally
	//be avoided for performance reasons, it is supported
	newEntity->node->setScale(toOgre(data->scale));

	//this is a special case, where we have a model attached to a skeleton bone, but no physics body
	if(string(boneName) != "")
	{	
		player->skeleton.attachDependantModel(boneName, id);
	}
}



//When a ragdoll is loaded, this function is called.  Use it to load or designate the model and to start
//setting up your ragdoll calculations
void loadSkeletalCallback(Scythe::PhysicalObject*, const char* modelName, const Scythe::PhysicsModel* data, void* userData, Scythe::SkeletonData* skelID, const Scythe::Vector3 position, const Scythe::Matrix rotation)
{
	//this is your character
	player = new ACharacterClass;
	Ogre::Entity* ent = loadModel("zombie2.mesh", Ogre::Vector3(0,0,0));	//load the actual model

	ent->getParentSceneNode()->setPosition(toOgre( position ));

	//our skeleton setup function that makes a hierarchal list of all the bones (for later use)
	//we give it a pointer to the entity model, and the skeleton ID we got from scythe
	player->skeleton.setup(ent, skelID);

}



// called when scythe wants you to create a light
void loadLightCallback(Scythe::PhysicalObject* physObj, const Scythe::PhysicsLight* data, const int id)
{
	Ogre::Light* light = 0;
	//create light
	if(data->type == Scythe::LIGHT_SPOT)
		light = loadLight(true);
	else
		light = loadLight(false);

	if(!light) return;

	//store data
	ALightClass* newLight = new ALightClass;
	newLight->light = light;
	newLight->node = light->getParentSceneNode();
	newLight->scytheID = id;

	//add it to our physics object class
	PhysicsObject* po = (PhysicsObject*)physObj;		//need to cast it to your own type
	po->lights.push_back( newLight );

	//set color
	light->setDiffuseColour(Ogre::ColourValue( data->RGB[0], data->RGB[1], data->RGB[2] ));
	light->setSpecularColour(Ogre::ColourValue( data->RGB[0], data->RGB[1], data->RGB[2] ));
}


